package models;

import play.mvc.*;
import play.libs.*;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.actor.*;
import static akka.pattern.Patterns.ask;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.util.*;

import models.msgs.*;
import models.msgs.Number;
import static java.util.concurrent.TimeUnit.*;

public class Table extends UntypedActor {
    
    // Default table.
    static ActorRef defaultTable = Akka.system().actorOf(Props.create(Table.class));
    
    // Members of this table.
    static Map<String, ActorRef> members = new HashMap<String, ActorRef>();
    static int maxMembers = 2;    //gra pokerowa bedzie sie sie odbywac pomiedzy 2 graczami!
    static int memb = 0;          //poczatkowa wartosc graczy!

    static int max = 2;
    static String[] name_list = new String[max];
    static playerInfo[] Players = new playerInfo[max];                //tablica w ktorej bedziemy przechowwac informacje dotyczace graczy!
    static playerInfo player;
    static ArrayList<Cards> myDeck;
    static int PlayersLeft = 0;
    static int ActiveLeft = 0;
    static Cards card;
    static int Dealer,MC,DC,Turn,BANK,MIN,AllinCount;
    static int[] Moved;
    static int done;
    public int Objective = 0;
   

    public static void join(final String name, WebSocket.In<JsonNode> in, WebSocket.Out<JsonNode> out ) throws Exception{
        int i = 0;
        i = i + memb;
        Moved = new int[maxMembers];

        name_list[memb] = name;                  //w name_list bedziemy przechowywac imiona graczy, w tym wypadku 2.
        Players[i] = new playerInfo(name,memb);  //wkladamy do tablicy players informacje! ---->dostep w tablicy Players[]!
        memb = memb + 1;
        if(memb > maxMembers)
            return;
        // Send the Join message to the table
        String result = (String)Await.result(ask(defaultTable,new Join(name), 1000), Duration.create(1, SECONDS));

             
        



                
        if("OK".equals(result)) {
        	
            ActorRef player =  Akka.system().actorOf(
                    Props.create(Human.class, name, in, out,  defaultTable ));
            
            members.put(name, player);            
            String text =  "my Actor has been added to Table"; 
            notifyAll(new Info(text,name));                             
        }


        if(memb == maxMembers){
           play();






    }
    

}


//--moje_metody---//

public static void play(){

     notifyAll(new Info("All players Joined!","SERVER")); 
     //notifyOne(Players[0],name_list[0]);
     notifyAll(new Info("Deck created!","SERVER")); 
     myDeck = Deck();                 //stworzylismy instacnje myDeck-ktora zawiera wszystkie karty!
     PlayersLeft = memb;
     Dealer = 0;
     BANK = 0;  //nasza pula w grze/

   
     
     
     for(int i=0;i<=memb-1;i++)             //Need to distribute cards
                    if(Players[i].isAlive())  
                        for(int j=0;j<4;j++){
                            card = Distribution(myDeck); 
                             Players[i].takeCards(j, card);}
                        
             
    notifyAll(new Info("DISTRIBUTION FINISHED!","SERVER"));
    //czas ustalic dealera i BG,SB i inne!

   MC=SB(Dealer,memb);                 //ustalamy ture,small blind,big blind!
   DC=BB(Dealer,memb,MC);
   Turn = Turn(DC,memb);
   BANK =  Players[MC].SmallBLind() + Players[DC].BigBlind();
   notifyAll(new Info("BANK is: " + BANK + " and player: " + name_list[Turn]+ " is having a turn!" ,"SERVER"));

   //czas wyslac informacje do graczy!

   for(int i = 0;i<memb;i++)
        if(Players[i].isAlive())
            notifyOne(Players[i],name_list[i]);







//playing = true;                    //In first encounter,first player to join is becoming a dealer.
MIN = 30;
ActiveLeft = PlayersLeft;
AllinCount = 0;
int Rounds;
int Moves;

for(int i=0;i<=memb-1;i++)
         Moved[i] = 0;
Rounds = 1;
Moves = 0;
notifyAll(new Info("Round has started!","SERVER"));

//przygotowalismy graczy,mamy wyzerowany AllinCount,ActiveLeft,Min=30,moves and rounds,moved table
     
//while( (Rounds<5 && ActiveLeft>1) ||  (AllinCount >0 && Rounds<5) ){
                //
                //while( (Moves != ActiveLeft && ActiveLeft>1) && (Moves < ActiveLeft && ActiveLeft>1) || (AllinCount > 0 && ActiveLeft>=1) ) {
                   
                    //System.out.println("Moves count equal to: "+Moves);



         done = 0;

                   for(int j = 0; j<memb;j++)
                    if(Moved[j] == 0 )
                     for(int i=0;i<=memb-1;i++)
                        if(Players[i].isAlive())
                            if(i == Turn)
                                if(done == 0){
                                   notifyAll(new Info("Player of index "+ Players[Turn].getId() + " was given a turn","SERVER"));
                                   done = 1;
                                
                                    
                                    Players[i].setTurn(MIN);
                                    notifyOne(new Info("It's ur turn now!","SERVER"),name_list[i]);}         

                            
                   


            //wszyscy zrobili ruch! Czas poimformwoac graczy o ich stanie gry!
                    

        















}



//members.put(name, player);

 static public void notifyOne(Object msg,String name)
    {
       
              members.get(name).tell(msg, defaultTable);

    }
    




public static ArrayList<Cards> Deck(){
    ArrayList<Cards> Deck = new ArrayList<Cards>(); //we create ArrayList of Cards
    Cards card;
    
    for(int i=0;i<=3;i++)                       //we create all cards by 4 colours
        for(int j = 1;j<=13;j++){
            card = new Cards(i,j);
            Deck.add(card);
            
        }
    
    return Deck;
}                                 



public static Cards Distribution(ArrayList<Cards> D){
    Cards c;                        //card which will be given to player
    Random rnd = new Random();
    int x;

            x = rnd.nextInt(D.size());
            c = D.get(x);
            D.remove(x);
    
    return c;
        
    
}  





public static int BB(int dealer,int PL,int MC){
        int i;
        if(MC==PL-1)
            i=0;
        else
            i=MC+1;
        
        
                while(true){
                    if(Players[i].isAlive())
                        return i;
                    else
                        if(i < PL-1)
                            i++;
                        else
                            i=0;
            
            }
    }
//-----------------Method_to_determine_SmallBlind----------------------//
public static int SB(int dealer,int memb){   
    int i;
    if(dealer==memb-1)
        i=0;
    else
        i=dealer+1;
    
    while(true){
        if(Players[i].isAlive())
            return i;
        else{
            if(i < memb-1)
                i++;
            else
                i=0;}
    }
}

public static int Turn(int DC,int memb){
    
    int i;
    if(DC==memb-1)
        i=0;
    else
        i=DC+1;

    
    while(true){
        if(Players[i].isAlive())
            return i;
        else{
            if(i < memb)
                i++;
            else
                i=0;}
    }
}





public static void  NewTurn(){


                done = 0;

                   for(int j = 0; j<memb;j++)
                    if(Moved[j] == 0 )
                     for(int i=0;i<=memb-1;i++)
                        if(Players[i].isAlive())
                            if(i == Turn)
                                if(done == 0){
                                   notifyAll(new Info("Player of index "+ Players[Turn].getId() + " was given a turn","SERVER"));
                                   done = 1;
                                
                                    
                                    Players[i].setTurn(MIN);
                                    notifyOne(new Info("It's ur turn now!","SERVER"),name_list[i]);}



                 


}

public static int  CHECK(){

    for(int i = 0; i<memb;i++)
        if(Moved[i] == 0)
          return  0;

 return 1 ;      
}




public static int WHO(String name){

    for(int i=0;i<memb;i++)
        if(name_list[i] == name)
            return i;


return -1;  //error
}

public static int Code(int number){

    String comm = "" + number;
    char order;

    order = comm.charAt(0);
    if(order == '5')
        return 1;  //dobrze 

    return 0;         //zle
}

public int[] Exchange(int number ){

    String comm = "" + number;
    //w pod coomm bedziemy mieli nasz numerek
    int Ex[] = new int[4];

    for(int i = 1; i<5;i++)
        Ex[i] = 0;          //Ex 0,0,0,0

    for(int i =0;i<4;i++ )
        if(comm.charAt(i) == '1' )
            Ex[i] = 1;

return Ex;


}

public void CardsExchange(int[] Ex,int x){  //przekazujemy tablice wartosc co chcemy wymienic i komu (I-index)

for(int i =0;i<4;i++)
    if(Ex[i]==1){
    card = Distribution(myDeck); 
    Players[x].takeCards(i, card);}

    notifyAll(new Info("TROOLLLOO","SERVER"));



}


public int TakeMoney(String m){
String money = m;
String moneyGot = ""; //moneyGot to co my mamy

if(money == "CALL")
    //min pozostaje jak byl
   return  Players[Turn].Call();
else if(money == "RAISE"){
     for(int i = 5;i<money.length();i++)
        moneyGot = moneyGot + money.charAt(i);
     return  Players[Turn].Raise(Integer.parseInt(moneyGot));}
else if(money == "ALLIN")
     return  Players[Turn].Allin();
else if(money == "PASS")
     return Players[Turn].Pass();
else 
     return -1;
}




public String  MoveMoney(int command)   //commm co robimy, x ile pieniedzy 
{
    String comm = "" + command;
    char order;
    String money = "";

    order = comm.charAt(0);
    for(int i = 1;i<comm.length();i++){
        money = money + comm.charAt(i);
    }

    //notifyAll(new Info("Number got:  " + order ,"SERVER"));
    if(order == '0')   //pass
        return "PASS";
    else if(order == '1')   //call
        return "CALL";
    else if(order == '2')   //raise
        return "RAISE"+ money;
    else if (order == '3')   //all-in
        return "ALLIN";

    else
        return "ERROR";
}







    //-----------------------------------------------------------------------------------------------------------------------------//



    public static void quit(final String username) throws Exception{

        //to do
    }
    
       
    public void onReceive(Object message) throws Exception {
        if(message instanceof Join) {
            
            Join join = (Join) message;
                        
            if(false){
            	// if username is already taken do sth        	
            }
            else {                
                getSender().tell("OK", getSelf());
            }
        } else if (message instanceof Number)
        {   
            

           

            String money;
            int moneyTaken;
        	int nr = ((Number) message).getNumber();
            //notifyAll(new Info("Number got:  " + nr ,"SERVER"));
        	String name = ((Number) message).getName();  
           // if(name != name_list[Turn])

            int[] exchange;
            int Check = Code(nr);  //check-kod do sprawdzenai czy to inforacja o zmianie karty
            notifyAll(new Info("CHECK code is equal to: " + Check ,"SERVER"));
                exchange = Exchange(nr);
                CardsExchange(exchange,WHO(name));
                

            
           

            money = MoveMoney(nr);
             //notifyAll(new Info("Number got:  " + money ,"SERVER"));
            moneyTaken = Players[Turn].getStake();
            BANK = BANK + TakeMoney(money);

            if(MIN < moneyTaken)
                MIN = moneyTaken;


            notifyAll(new Info("Player "+ Players[Turn].getName() + "made move:  " + money ,"SERVER"));
            Moved[Turn] = 1;  //gracz sie ruszyl!
            nr = CHECK();

            if(nr == 1){
                notifyAll(new Info("ALL PLAYERS MADE MOVE! TIME for exchange cards","SERVER"));
                Objective = 1;
            }
            
            

            Turn = Turn(Turn,memb);   //nowa ture wyszukujemy!  //mamy tez nowy min  i zaktualizowany BANK!
            NewTurn();
           // mama();  //resetujemy done
           //done = 0;
        	//getSender().tell(Deck.giveCard(nr), getSelf());
        	//String text = "I've sent    " +  Deck.giveCard(nr).getRank() + " " + Deck.giveCard(nr).getColor() + "    to " + name;
        	//notifyAll(new Info(text,"Table"));



        



        }
        else if (message instanceof Info)
        {   
        	String text = ((Info) message).getText();
        	String name = ((Info) message).getName();  
        	
        	notifyAll(new Info(text, name));
        }  
        else if(message instanceof Quit)  {            
           
            String name = ((Quit)message).getName();            
            members.remove(name);
            String text = "has left the game";            		
            notifyAll(new Info(text, name));
          
        
        } else {
            unhandled(message);
        }
        
    }
    
        
    static public void notifyAll(Object msg)
    {
        for (ActorRef member : members.values())
        {
            member.tell(msg, defaultTable);
        }
    }
    
    
    
    
}

